﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shell;

using Newtonsoft.Json;

using AutoTranslate.Helps;
using AutoTranslate.Translators;

namespace AutoTranslate {
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window {
        [DllImport("User32.dll")]
        protected static extern int SetClipboardViewer(IntPtr hWndNewViewer);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        private IntPtr nextClipboardViewer;
        private Storyboard animate;
        private readonly Dictionary<Engine, ITranslator> translators;

        public MainWindow() {
            InitializeComponent();

            translators = Assembly
                .GetExecutingAssembly()
                .GetTypes()
                .Where(type => typeof(ITranslator).IsAssignableFrom(type) && !type.IsAbstract)
                .Select(type => (ITranslator) Activator.CreateInstance(type))
                .ToDictionary(translator => translator.Engine, translator => translator);

            foreach (var translator in translators.Values.OrderBy(x => x.Order).ThenBy(x => x.Name))
            {
                var subMenu = new MenuItem {
                    Header = translator.Name,
                    ToolTip = translator.Hint,
                    Tag = translator
                };
                subMenu.SetBinding(MenuItem.IsCheckedProperty, new Binding("Engine")
                {
                    Converter = new EnumBooleanConverter(),
                    ConverterParameter = translator.Engine
                });
                menuEngines.Items.Add(subMenu);
            }

            if (File.Exists(@"Config.json")) {
                using (var fileReader = new StreamReader(@"Config.json")) {
                    Config = JsonConvert.DeserializeObject<Config>(fileReader.ReadToEnd());
                }
                if (Config.Font != null) {
                    FontHelper.SetFont(textShowing, Config.Font);
                    FontHelper.SetColor(textShowing, Config.Color);
                }
            }
            if (!Config.GlassWindow || !Config.HasDwmApi) {
                AllowsTransparency = true;
            }
        }

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
            // defined in winuser.h
            const int WM_DRAWCLIPBOARD = 0x308;
            const int WM_CHANGECBCHAIN = 0x030D;

            switch (msg) {
                case WM_DRAWCLIPBOARD:
                    DisplayClipboardData();
                    SendMessage(nextClipboardViewer, msg, wParam, lParam);
                    break;

                case WM_CHANGECBCHAIN:
                    if (wParam == nextClipboardViewer)
                        nextClipboardViewer = lParam;
                    else
                        SendMessage(nextClipboardViewer, msg, wParam, lParam);
                    break;
            }
            return IntPtr.Zero;
        }

        private async void DisplayClipboardData() {
            if (Clipboard.ContainsText()) {
                animate.Begin();
                taskBarItemInfo.ProgressState = TaskbarItemProgressState.Indeterminate;
                var text = Clipboard.GetText();
                try {
                    textShowing.Text = await translators[Config.Engine].Translate(text);
                } catch (TaskCanceledException) {
                    // Eat this
                } catch {
                    textShowing.Text = "连接服务器或解析服务器返回数据失败！";
                }
                textShowing.ToolTip = text;
                animate.Stop();
                taskBarItemInfo.ProgressState = TaskbarItemProgressState.None;
            }
        }

        protected override void OnSourceInitialized(EventArgs e) {
            base.OnSourceInitialized(e);

            var hwnd = new WindowInteropHelper(this).Handle;

            var hwndSource = HwndSource.FromHwnd(hwnd);
            Debug.Assert(hwndSource != null, "hwndSource != null");

            if (Config.GlassWindow && Config.HasDwmApi) {
                Debug.Assert(hwndSource.CompositionTarget != null, "hwndSource.CompositionTarget != null");
                hwndSource.CompositionTarget.BackgroundColor = Colors.Transparent;

                var policy = DwmApi.DwmNcrenderingPolicy.DWMNCRP_USEWINDOWSTYLE;

                DwmApi.DwmSetWindowAttribute(hwnd,
                    DwmApi.DwmWindowAttribute.DWMWA_NCRENDERING_POLICY,
                    ref policy,
                    (uint) sizeof (DwmApi.DwmNcrenderingPolicy));
                DwmApi.DwmEnableBlurBehindWindow(hwnd, new DwmApi.DwmBlurbehind {
                    dwFlags = DwmApi.DwmBlurbehind.DwmBb.DWM_BB_ENABLE,
                    fEnable = true,
                    hRegionBlur = IntPtr.Zero
                });
            }

            hwndSource.AddHook(WndProc);
            nextClipboardViewer = (IntPtr)SetClipboardViewer(hwnd);
        }

        private void Window_Loaded(object sender, RoutedEventArgs e) {
            animate = (Storyboard)FindResource("Animation");
        }

        private void Exit_Click(object sender, RoutedEventArgs e) {
            Close();
        }

        private void SetFont_Click(object sender, RoutedEventArgs e) {
            using (var fd = new System.Windows.Forms.FontDialog { ShowColor = true }) {
                fd.Font = FontHelper.GetFont(textShowing);
                fd.Color = FontHelper.GetColor(textShowing);
                if (fd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
                    FontHelper.SetFont(textShowing, fd.Font);
                    FontHelper.SetColor(textShowing, fd.Color);
                }
            }
        }

        private void Ellipse_MouseDown(object sender, MouseButtonEventArgs e) {
            if (e.LeftButton == MouseButtonState.Pressed) {
                this.DragMove();
            } else if (e.MiddleButton == MouseButtonState.Pressed) {
                DisplayClipboardData();
            }
        }

        private Config Config {
            set { this.DataContext = value; }
            get { return this.DataContext as Config; }
        }

        protected override void OnClosed(EventArgs e) {
            if (Config.AutoSave) {
                Config.Font = FontHelper.GetFont(textShowing);
                Config.Color = FontHelper.GetColor(textShowing);
                using (var writer = new StreamWriter(@"Config.json")) {
                    writer.Write(JsonConvert.SerializeObject(Config));
                }
            }

            base.OnClosed(e);
        }

        private async void menuEngines_SubmenuOpened(object sender, RoutedEventArgs e) {
            foreach (MenuItem subMenu in menuEngines.Items) {
                var translator = (ITranslator) subMenu.Tag;
                try {
                    var reply = await translator.Ping();
                    subMenu.Header = string.Format("{0} ({1}ms)", translator.Name, reply.RoundtripTime);
                } catch (TaskCanceledException) {
                    // Eat this
                }
            }
        }
    }

    public class Config {
        [JsonIgnore]
        public bool HasDwmApi {
            get {
                // .NET 4.5 doesn't support XP
                // if (Environment.OSVersion.Version.Major < 6)
                //    return false;
                bool result;
                DwmApi.DwmIsCompositionEnabled(out result);
                return result;
            }
        }

        public bool GlassWindow { get; set; }
        public double WindowMaxWidth { get; set; }
        public double WindowStartupLeft { get; set; }
        public double WindowStartupTop { get; set; }
        public System.Drawing.Font Font { get; set; }
        public System.Drawing.Color Color { get; set; }
        public Engine Engine { get; set; }
        public bool AutoSave { get; set; }
    }
}
